use serde::{Deserialize, Serialize};
use jwt::{decode, encode, Header, Validation, EncodingKey, DecodingKey};
use chrono::Utc;
use actix_web::dev::{Transform, Service, ServiceRequest, ServiceResponse};
use actix_web::{Error, error};
use async_graphql::futures_util::future::{Ready, ok, Future};
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_web::http::HeaderValue;
use crate::entity::sys_user::SysUser;

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    pub user: SysUser,
    exp: usize,
}

/// 根据用户创建 token
pub fn create_token(mut usr: SysUser) -> String {
    usr.password = None; // 防止密码泄露
    // exp 是一个 utc timestamp，创建时在当前时间基础上往后延长多少时间
    let claim = Claims { user: usr, exp: (Utc::now().timestamp() + crate::CONFIG.token_exp) as usize };
    let secret = &crate::CONFIG.secure;

    encode(
        &Header::default(),
        &claim,
        &EncodingKey::from_secret(secret.as_bytes())
    ).unwrap_or(String::new())
}
/// 根据 token 验证，并返回用户
pub fn valid_token(token: &String) -> Option<SysUser> {
    let secret = &crate::CONFIG.secure;

    decode::<Claims>(
        token, &DecodingKey::from_secret(secret.as_bytes()),
        &Validation::default()
    ).map_or(None, |x| Some(x.claims.user))
}



// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
//    next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct AuthFilter;

// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S> for AuthFilter
    where
        S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
        S::Future: 'static,
        B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AuthHiMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(AuthHiMiddleware { service })
    }
}

pub struct AuthHiMiddleware<S> { service: S }

impl<S, B> Service for AuthHiMiddleware<S>
    where
        S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
        S::Future: 'static,
        B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let is_login = req.path().eq("/login");
        let token = req.headers()
            .get("Authorization")
            .unwrap_or(&HeaderValue::from_str("").unwrap())
            .to_str().unwrap_or("").to_string();
        let fut = self.service.call(req);

        Box::pin(async move {
            if is_login || !token.is_empty() && valid_token(&token).is_some() {
                Ok(fut.await?)
            } else {
                Err(error::ErrorUnauthorized("Unauthorized"))
            }
        })
    }
}